package org.web.demo.servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.util.Collection;
import java.util.Scanner;
import java.util.UUID;

/**
 * {@code @MultipartConfig} :
 * Servlet 3.0新增。此注释用于指示声明Servlet期望请求使用multipart/form-data MIME 类型。
 * 通过调用 HttpServletRequest.getPart 或 HttpServletRequest.getParts，使用MultipartConfig注释的 Servlet 检索给定multipart/form-data请求的Part组件。
 *
 * @author yexi
 */
@WebServlet("/fileUpload")
@MultipartConfig(
        //Container 存放临时文件的位置
        location = "",
        //上传文件允许的最大大小（以字节为单位）
        maxFileSize = 8000000,
        //multipart/form-data允许的最大请求大小
        maxRequestSize = 14000000,
        //文件将写入磁盘的大小阈值
        fileSizeThreshold = 0
)
@Slf4j
public class FileUploadServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 文件数据是通过二进制提交的，使用 getParameter 方法是无法获取到的。
        log.info(req.getParameter("headImg"));

        // 以流方式获取
        Scanner sc = new Scanner(req.getInputStream());
        while (sc.hasNextLine()) {
            log.info(sc.nextLine());
        }

        try {
            //返回所有上传部件的集合。
            Collection<Part> parts = req.getParts();
            for (Part part : parts) {
                //获取浏览器传递的内容类型。如果未定义，则为null。
                String contentType = part.getContentType();
                //如果判断出上传的是文件
                if (contentType != null) {
                    // 判断上传的文件类型是否合法(这个所谓合法其实是根据文件的扩展名来判断的，所以将扩展名改为图片格式也能伪装上传）
                    if (!part.getContentType().startsWith("image/")) {
                        req.setAttribute("errorMsg", "请上传图片");
                        req.getRequestDispatcher("/upload.jsp").forward(req, resp);
                        return;
                    }

                    // 如果此part代表上传的文件，则获取上传中提交的文件名。如果没有可用的文件名或此part不是文件上传，则返回null。
                    String realFileName = part.getSubmittedFileName();
                    log.info("上传者的IP地址: " + req.getRemoteAddr());
                    log.info("上传文件名" + realFileName);

                    //视业务而定: 随机生成文件名
                    // 获取上传文件扩展名
                    String ext = realFileName.substring(realFileName.lastIndexOf("."));
                    // 生成唯一字符串拼接文件名(可用来处理文件名相同覆盖现有文件的现象)
                    String fileName = UUID.randomUUID().toString() + ext;

                    // 获取项目下的 upload 目录的绝对路径，拼接成文件的保存路径
                    String realPath = this.getServletContext().getRealPath("/upload") + "/" + fileName;
                    part.write(realPath);
                } else {
                    //获取该part对应的multipart表单中的字段名称。
                    log.info(req.getParameter(part.getName()));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
            req.setAttribute("errorMsg", "文件太大了");
            req.getRequestDispatcher("/upload.jsp").forward(req, resp);
        }

    }
}